package server;

/**
 *
 * @author Administrator
 */
import java.awt.AWTException;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.Robot;
import java.awt.Toolkit;
import java.awt.event.KeyEvent;
import java.awt.image.BufferedImage;

import com.sun.image.codec.jpeg.JPEGCodec;
import com.sun.image.codec.jpeg.JPEGImageEncoder;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Arrays;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.logging.Level;
import java.util.logging.Logger;

@SuppressWarnings("restriction")
public class AndroidServer {

	private static final long serialVersionUID = 1L;
	private static ServerSocket serverSocket;
	private Robot robot;
	private Sender sender;
	private Receiver receiver;
	private CommandHandler handler;
	private ScreenCapturer capturer;
	private boolean isNyditot = false;
	private byte[] screenBuffer;
	private LinkedBlockingQueue<int[]> commands = new LinkedBlockingQueue<int[]>();
	private int deviceWidth = 320;
	private int deviceHeigth = 480;
	private double normalScale;
	private double nyditotScale;

	private final static Rectangle r = new Rectangle(Toolkit
			.getDefaultToolkit().getScreenSize());

	public static void main(String[] args) {
		new AndroidServer().startService();
	}

	public boolean startService() {
		try {
			serverSocket = new ServerSocket(54321);
			Socket socket = serverSocket.accept();
			System.out.println("accept:");
			DataInputStream input = new DataInputStream(
					new BufferedInputStream(socket.getInputStream()));
			DataOutputStream output = new DataOutputStream(
					new BufferedOutputStream(socket.getOutputStream()));
			output.flush();
			sender = new Sender(socket, output);
			receiver = new Receiver(socket, input);
			handler = new CommandHandler();
			capturer = new ScreenCapturer();

			receiver.start();
			capturer.start();
			handler.start();
			Thread.sleep(250);
			sender.start();
		} catch (Exception e) {
			e.printStackTrace();
			System.exit(0);
		}
		return true;
	}

	class ScreenCapturer extends Thread {
		@Override
		public void run() {
			while (true) {
				try {
					captureScreen();
					Thread.sleep(800);
				} catch (Exception e) {
					e.printStackTrace();
					continue;
				}
			}
		}

		private void captureScreen() {
			BufferedImage mBufferedImage = ImageToolkit.scaledScreen(
					deviceWidth, deviceHeigth, isNyditot);
			ByteArrayOutputStream bos = new ByteArrayOutputStream();
			JPEGImageEncoder encoder = JPEGCodec.createJPEGEncoder(bos);
			try {
				encoder.encode(mBufferedImage);
				byte[] data = bos.toByteArray();
				bos.close();
				screenBuffer = data;
			} catch (IOException e) {
				Logger.getLogger(AndroidServer.class.getName()).log(
						Level.SEVERE, null, e);
				e.printStackTrace();
				System.exit(0);
			}
		}
	}

	class Sender extends Thread {
		private DataOutputStream out;
		private Socket socket;

		public Sender(Socket socket, DataOutputStream out) {
			this.out = out;
			this.socket = socket;
		}

		@Override
		public void run() {
			while (!socket.isClosed()) {
				synchronized (sender) {
					try {
						Thread.sleep(800);
						if (screenBuffer != null)
							sendScreen();
					} catch (Exception e) {
						e.printStackTrace();
						System.exit(0);
					}
				}
			}
		}

		public void sendScreen() {
			try {
				synchronized (screenBuffer) {
					out.writeInt(screenBuffer.length);
					out.write(screenBuffer);
				}
				out.flush();
			} catch (IOException e) {
				Logger.getLogger(AndroidServer.class.getName()).log(
						Level.SEVERE, null, e);
				e.printStackTrace();
				System.exit(0);
			}
		}

		public void sendImmediateScreen() {
			BufferedImage mBufferedImage = ImageToolkit.scaledScreen(
					deviceWidth, deviceHeigth, isNyditot);
			ByteArrayOutputStream bos = new ByteArrayOutputStream();
			JPEGImageEncoder encoder = JPEGCodec.createJPEGEncoder(bos);

			try {
				encoder.encode(mBufferedImage);
				byte[] data = bos.toByteArray();

				bos.close();
				out.writeInt(data.length);
				out.write(data);
				System.out.println(data.length);
			} catch (Exception e) {
				Logger.getLogger(AndroidServer.class.getName()).log(
						Level.SEVERE, null, e);
				e.printStackTrace();
				System.exit(0);
			}
		}
	}

	class Receiver extends Thread {
		private DataInputStream in;
		private Socket socket;

		public Receiver(Socket socket, DataInputStream in) {
			try {
				robot = new Robot();
			} catch (AWTException e) {
				e.printStackTrace();
			}
			this.in = in;
			this.socket = socket;
		}

		@Override
		public void run() {
			while (!socket.isClosed()) {
				try {
					int type = in.readInt();
					if (type < 100) {
						systemInfoHandler(type, in);
					} else if (type < 200) {
						int[] command = new int[] { type, in.readInt(),
								in.readInt(), in.readInt() };
						synchronized (commands) {
							commands.put(command);
							commands.notify();
						}
					} else if (type > 200) {
						System.out.println(type);
						actionHandler(type);
					}
				} catch (Exception e) {
					e.printStackTrace();
					System.exit(0);
				}
			}
		}

		private void actionHandler(int type) {
			switch (type) {
			case 211:
				nextSlide();
				break;
			case 212:
				preSlide();
				break;
			case 301:
				arrowPointer();
				break;
			case 302:
				penPointer();
				break;
			case 303:
				eraserPointer();
				break;
			case 401:
				refreshScreen();
				break;
			case 402:
				rotateScreen();
				break;
			}
		}

		private void penPointer() {
			groupCtrl(KeyEvent.VK_P);
			System.out.println("Now It's Pen Pointer");
		}

		private void eraserPointer() {
			groupCtrl(KeyEvent.VK_E);
			System.out.println("Now It's Eraser Pointer");
		}

		private void arrowPointer() {
			groupCtrl(KeyEvent.VK_A);
			groupCtrl(KeyEvent.VK_U);
			System.out.println("Now It's Arrow Pointer");
		}

		private void preSlide() {
			robot.mouseWheel(-1);
			System.out.println("go to the pre slide");
			sender.sendImmediateScreen();
		}

		private void nextSlide() {
			robot.mouseWheel(1);
			System.out.println("go to the next slide");
			sender.sendImmediateScreen();
		}

		private void refreshScreen() {
			sender.sendImmediateScreen();
		}

		private void rotateScreen() {
			isNyditot = !isNyditot;
			sender.sendImmediateScreen();
		}

		private void groupCtrl(int option) {
			robot.keyPress(KeyEvent.VK_CONTROL);
			robot.keyPress(option);
			robot.keyRelease(option);
			robot.keyRelease(KeyEvent.VK_CONTROL);
		}

		private void systemInfoHandler(int type, DataInputStream dis) {
			BufferedReader br = new BufferedReader(new InputStreamReader(dis));
			try {
				String info = br.readLine();
				String[] infos = info.split("[,=\\{\\}]");
				System.out.println(Arrays.toString(infos));
				deviceHeigth = (int) (Integer.parseInt(infos[6].trim()) * 1.5);
				deviceWidth = (int) (Integer.parseInt(infos[4].trim()) * 1.5);
				double d = deviceHeigth * 1.0 / deviceWidth;
				double s = r.width * 1.0 / r.height;
				normalScale = r.width * 1.0 / deviceWidth;
				if (d >= s)
					nyditotScale = r.height * 1.0 / deviceWidth;
				else
					nyditotScale = r.width * 1.0 / deviceHeigth;
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
	}

	class CommandHandler extends Thread {
		@Override
		public void run() {
			while (true) {
				synchronized (commands) {
					while (commands.isEmpty()) {
						try {
							commands.wait();
						} catch (InterruptedException e) {
							e.printStackTrace();
						}
					}
					int[] command = commands.poll();
					Point p = translateCoordinate(command);
					touchCommandHandler(command[0], command[1], p.x, p.y);
				}
			}
		}

		private Point translateCoordinate(int[] command) {
			int x = command[2];
			int y = command[3];
			if (!isNyditot) {
				x = (int) (x * normalScale);
				y = (int) (y * normalScale);
			} else {
				int temp = x;
				x = (int) (nyditotScale * y);
				y = (int) (r.height - nyditotScale * temp);
			}
			Point p = new Point(x, y);
			return p;
		}

		private void touchCommandHandler(int type, int key, int valuex,
				int valuey) {
			System.out.println("key=" + type + ":" + "(" + valuex + ","
					+ valuey + ")");
			switch (type) {
			case 101:
				robot.mouseMove(valuex, valuey);
				robot.mousePress(key);
				break;
			case 102:
				robot.mouseMove(valuex, valuey);
				break;
			case 103:
				robot.mouseRelease(key);
				break;
			default:
				break;
			}
		}
	}
}
